#include "way.h"
int west=0;	//全局变量，保存向西通过的车
int east=0;
Condition::Condition(Sema *sema1,Sema *sema2)
{
	queue1=sema1;
	queue2=sema2;
}
OneWay::OneWay(int maxC)
{
	key_t shm_key=101,sem_key=201;
	int ipc_flg = IPC_CREAT | 0644;
	rate=1;//设定车速
	//为最大同向车数申请共享空间
	if((maxCars=(int *)set_shm(shm_key++,1,ipc_flg)) == NULL)
	{
		perror("Share memory create error");
		exit(EXIT_FAILURE);
	}
	*maxCars=maxC;	//设置最大同向车数
	//为当前通过的车辆数申请共享空间
	if((numCars=(int *)set_shm(shm_key++,1,ipc_flg)) == NULL)
	{
		perror("Share memory create error");
		exit(EXIT_FAILURE);
	}
	*numCars=0;//一开始没有车通过
	//为当前通过的车辆方向申请共享空间
	if((currentDirec=(int *)set_shm(shm_key++,1,ipc_flg)) == NULL)
	{
		perror("Share memory create error");
		exit(EXIT_FAILURE);
	}
	*currentDirec=-1;//一开始没有方向
	Sema *sema1,*sema2;	//创建两个信号灯，初值为0
	int sem_id;
	if((sem_id = set_sem(sem_key++,0,ipc_flg)) < 0){
		perror("Semaphor create error");
		exit(EXIT_FAILURE);
	}
	sema1 = new Sema(sem_id);	//为东方通行申请信号灯
	if((sem_id = set_sem(sem_key++,0,ipc_flg)) < 0){
		perror("Semaphor create error");
		exit(EXIT_FAILURE);
	}
	sema2 = new Sema(sem_id);	//为西方通行申请信号灯
	OneWayFull=new Condition(sema1,sema2);//通过单行道的条件变量
	//最多有max个车辆同时行驶
	if((sem_id = set_sem(sem_key++,maxC,ipc_flg)) < 0){
		perror("Semaphor create error");
		exit(EXIT_FAILURE);
	}
	sema1 = new Sema(sem_id);
	lock = new Lock(sema1);//单行道管程锁
}
void Condition::Wait(Lock *conditionLock,int direc)
{
		conditionLock->open_lock();
		if(direc==0) queue1->down();//进入所在方向等待队列睡眠
		else queue2->down();
		conditionLock->close_lock();//被唤醒后重新获取锁
}
void Condition::Signal(int direc)
{
	if(direc==0) queue1->up();//唤醒等待者
	else queue2->up();
}
void OneWay::Arrive(int direc)
{
	lock->close_lock();//获取管程锁
	while((direc!=*currentDirec&&*numCars!=0)||*numCars>=*maxCars){	//如果不满足被唤醒的情况,即不同向并且车数不为0或者车数已经达到最大值
		cout<<getpid()<<"号车向"<<dir[direc]<<"等待单行道"<<endl;
		OneWayFull->Wait(lock,direc);//在条件变量上等待,直到等到通行的许可
	}
	(*numCars)++;//单行道上车辆数加一
	cout<<getpid()<<"号车向"<<dir[direc]<<"进入单行道"<<endl;//打印进入情况
	*currentDirec=direc;//当前方向为我的方向
	lock->open_lock();//释放管程锁
}

void OneWay::Quit(int direc)
{
	lock->close_lock();//获取管程锁
	(*numCars)--;//单行道上车辆数减1
	cout<<getpid()<<"号车向"<<dir[direc]<<"离开单行道"<<endl;//打印离开信息
	OneWayFull->Signal(!direc);//唤醒相反队列中的等待者
	
	//}
	lock->open_lock();//释放管程锁
}
void OneWay::Cross(int direc)
{
	cout<<getpid()<<"号车向"<<dir[direc]<<"通过单行道,道上车数："<<*numCars<<endl;//打印车辆行驶状况
	sleep(rate);
}
//移动函数
void move(OneWay * way,int dire)
{
	way->Arrive(dire);//单行道管程->Arrive(direc)
	way->Cross(dire);//单行道管程->Cross(direc)
	way->Quit(dire);//单行道管程->Quit(direc)
}
int *pid,pn;
//递归创建子进程
void create_process(int n,int *a,OneWay * way)
{
	int status;
	pid[n-1]=fork();
	if(pid[n-1]==0){
		move(way,a[n-1]);
		exit(0);
	}else{
		if(n==1){
			for(int i=0;i<pn;i++)
				waitpid(pid[i],&status,0);
			cout<<east<<"辆向东"<<west<<"辆向西，正常通过"<<endl;
		}else{
			create_process(n-1,a,way);
		}
	}
}
int main(int argc,char ** argv)
{
	int carN,maxN;//分别表示车的辆数和最大车数
	if(argc==1){
		carN=2;maxN=1;
	}else{
		carN=atoi(argv[1]);
		maxN=atoi(argv[2]);
	}
	//建立多个行车子进程(最好不少于5个)，每个随机设定一个行车方向
	srand((unsigned) time(NULL)); //用时间做种，每次产生随机数不一样
	int *a=new int[carN];
	west=0;
	for(int i=0;i<carN;i++){
		a[i]=rand()%2;
		west+=a[i];
	}
	east=carN-west;
	OneWay * way=new OneWay(maxN);//建立单行道管程
	//每个进程做：
	pn=carN;
	pid=new int[pn];
	create_process(carN,a,way);
	return 0;
}


